# !/usr/bin/env python3
# -*- coding: utf-8 -*-

import random

from PySide6 import QtCore
from PySide6.QtCore import QSize, QPoint, QLine, QPointF, QLineF, SIGNAL, Signal
from PySide6.QtGui import QIcon, QMouseEvent, Qt, QPainter, QColor, QPen, QColorConstants, QBrush, QPolygonF, \
    QPainterPath, QStandardItemModel, QStandardItem, QFont
from PySide6.QtWidgets import QPushButton, QMainWindow, QWidget, QLabel, QListView, QListWidget, QMessageBox, \
    QListWidgetItem, QFrame
from UI.ui_lib.color import colorList


class TSNButton(QPushButton):
    def __init__(self,
                 title,
                 parent: QWidget,
                 node_id,
                 node_type):
        super().__init__(title, parent)

        self.parent = parent
        self.setMouseTracking(True)

        self.node_id = node_id
        self.node_type = node_type
        self.node_name = '%s%s' % (self.node_type,
                                   self.node_id)

        self.label = QLabel(self.node_name, parent)
        self.label.setMaximumSize(40, 20)
        self.label.setMinimumSize(40, 20)
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setStyleSheet("QLabel{font:bold;"
                                 "font-size:15px;}")
        self.label.show()

        self.sched_label = QLabel('', parent)
        self.sched_label.setMaximumSize(40, 25)
        self.sched_label.setMinimumSize(40, 25)
        self.sched_label.setAlignment(Qt.AlignCenter)
        self.sched_label.setStyleSheet("QLabel{font:bold;"
                                       "font-size:24px;}")

        self.sched_label.show()

        self.icon = QIcon()
        if node_type == 'SW':
            self.icon.addFile('img/sw_icon.png')
        elif node_type == 'NIC':
            self.icon.addFile('img/es_icon.png')

        self.setMaximumSize(40, 40)
        self.setMinimumSize(40, 40)
        self.setIcon(self.icon)
        self.setIconSize(QSize(30, 30))
        self.setStyleSheet("QPushButton{background:rgba(0,0,0,0);"
                           "border:1px solid rgba(0,0,0,0);}"
                           "QPushButton:hover{background-color:rgb(220,20,60);"
                           "border:none;color:rgb(220,20,60);} "
                           "QPushButton:checked{background-color:red;"
                           "border:none;color:rgb(0,0,0);}")
        self.setCursor(Qt.PointingHandCursor)
        x = random.randint(0, 400)
        y = random.randint(0, 400)
        self.iniDragCor = [x, y]
        self.iniDragCorLabel = [x, y + 40]
        self.point = QPoint(*self.iniDragCor)
        #
        self.move(self.point)
        self.label.move(*self.iniDragCorLabel)
        self.sched_label.move(self.iniDragCor[0],
                              self.iniDragCor[1] - 20)
        self.children_button = []

    def mousePressEvent(self, e):
        # self.setCursor(Qt.PointingHandCursor)
        self.iniDragCor[0] = e.position().x()
        self.iniDragCor[1] = e.position().y()

    def mouseMoveEvent(self, e: QMouseEvent):
        if e.buttons() == QtCore.Qt.LeftButton:
            self.setCursor(Qt.SizeAllCursor)

            x = e.position().x() - self.iniDragCor[0]
            y = e.position().y() - self.iniDragCor[1]

            cor = QPoint(x, y)
            label_cor = QPoint(x, y + 40)
            sched_cor = QPoint(x, y - 40)
            parent_cor = self.mapToParent(cor)
            self.point = parent_cor
            self.move(parent_cor)  # 需要mapToParent一下才可以的,否则只是相对位置。
            self.label.move(self.mapToParent(label_cor))
            self.sched_label.move(self.mapToParent(sched_cor))
            self.parent.update()
            # self.setCursor(Qt.OpenHandCursor)

    def mouseReleaseEvent(self, e):
        if e.button() == QtCore.Qt.LeftButton:
            self.iniDragCor[0] = e.position().x()
            self.iniDragCor[1] = e.position().y()
            # print('release', self.iniDragCor[0], self.iniDragCor[1])
            self.parent.update()
            self.setCursor(Qt.PointingHandCursor)

    def enterEvent(self, event):
        # super().enterEvent(event)#6DDF6D
        self.setStyleSheet(
            '''QPushButton{background:grey;border-radius:5px;}
                QPushButton:hover{background:grey;}''')

    def leaveEvent(self, event):
        # super().leaveEvent(event)
        self.setStyleSheet(
            '''QPushButton{background:rgba(0,0,0,0);border-radius:5px;}
                QPushButton:hover{background:grey;}''')

    def appendChildren(self,
                       child):
        self.children_button.append(child)


class topoPaintWidget(QWidget):
    def __init__(self,
                 parent,
                 main_window):
        super(topoPaintWidget, self).__init__(parent)

        self.parent = parent
        self.main_window = main_window

        self.qp = QPainter(self)
        self.repaint_button_path = []
        self.repaint_button_reverse_path = []
        self.repaint_pen = QPen()
        self.repaint_color = QColor()

        self.sched_label = []

        self.setGeometry(150, 150, 880, 450)

        # 给当前topoPaintWidget加边框
        self.styleWidget = QWidget(self)
        self.styleWidget.setGeometry(0, 0, 880, 450)
        self.styleWidget.setStyleSheet("QWidget{border:1px solid black;"
                                       "border-radius:5px; "
                                       "background: transparent;}")

        self.styleWidget.setWindowFlags(Qt.WindowStaysOnBottomHint)

        self.show()

    # 将节点之间的线添加到图中
    def paintEvent(self, event):
        qp = QPainter(self)
        qp.begin(self)
        # print(type(self.main_window))
        for tsn_button in self.main_window.tsn_button_set:
            parent_tsn_button = tsn_button
            for child_tsn_button in parent_tsn_button.children_button:
                p1_x = parent_tsn_button.point.x() + 20
                p1_y = parent_tsn_button.point.y() + 20
                p2_x = child_tsn_button.point.x() + 20
                p2_y = child_tsn_button.point.y() + 20
                if (parent_tsn_button, child_tsn_button) in self.repaint_button_path:
                    qp.setPen(self.repaint_pen)
                    # 画箭头
                    line = QLineF(QPoint(p1_x, p1_y),
                                  QPoint(p2_x, p2_y))
                    line.setLength(line.length() - 24)
                    v = line.unitVector()
                    v.setLength(10)  # 改变单位向量的大小，实际就是改变箭头长度
                    v.translate(QPointF(line.dx(), line.dy()))
                    n = v.normalVector()  # 法向量
                    n.setLength(n.length() * 0.5)  # 这里设定箭头的宽度
                    n2 = n.normalVector().normalVector()  # 两次法向量运算以后，就得到一个反向的法向量
                    p1 = v.p2()
                    p2 = n.p2()
                    p3 = n2.p2()
                    brush = QBrush()
                    brush.setColor(self.repaint_color)
                    brush.setStyle(Qt.SolidPattern)
                    qp.setBrush(brush)

                    qp.drawLine(line)
                    pl = QPolygonF([p1, p2, p3])
                    qp.drawPolygon(pl)

                elif (parent_tsn_button, child_tsn_button) in self.repaint_button_reverse_path:
                    qp.setPen(self.repaint_pen)
                    qp.drawLine(QPoint(p1_x, p1_y),
                                QPoint(p2_x, p2_y))
                else:
                    qp.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine))
                    qp.drawLine(QPoint(p1_x, p1_y),
                                QPoint(p2_x, p2_y))

        self.qp.end()


class streamListWidgetItem(QListWidgetItem):
    def __init__(self,
                 label,
                 parent,
                 path,
                 paint_widget: topoPaintWidget,
                 main_window):
        super(streamListWidgetItem, self).__init__('流量%s' % label, parent)

        self.label = label

        self.path = path

        font = QFont()
        font.setPointSize(13)
        self.setFont(font)

        self.paint_widget = paint_widget
        self.main_window = main_window
        self.color = random.choice(colorList)
        # (bnt1, bnt2), (bnt2, bnt3)这样的形式
        self.button_pair_path = []
        self.button_pair_reverse_path = []
        for item in path:
            parent_node_id = item[0]
            child_node_id = item[1]
            parent_button = None
            child_button = None
            for tsn_button in main_window.tsn_button_set:
                if tsn_button.node_id == parent_node_id:
                    parent_button = tsn_button
                if tsn_button.node_id == child_node_id:
                    child_button = tsn_button
            # 注意，两个方向的线都要画上，只画一个方向，线会被覆盖
            self.button_pair_path.append((parent_button, child_button))
            self.button_pair_reverse_path.append((child_button, parent_button))

        tsn_button_set = main_window.tsn_button_set
        self.sw_label = ['' for btn in main_window.tsn_button_set]

        plan_set = parent.plan_set
        # print(plan_set)
        for link in self.path:
            src_node = link[0]
            dst_node = link[1]
            for sched in plan_set:
                if sched['src_node_id'] == src_node and \
                        sched['dst_node_id'] == dst_node:
                    flow_set = sched['flow_set']
                    # 找到第一个匹配的流量即可，这是该流量在调度周期内的第一个报文
                    for flow in flow_set:
                        if flow['flow_id'] == self.label:
                            for btn_i in range(len(tsn_button_set)):
                                if tsn_button_set[btn_i].node_id == src_node:
                                    self.sw_label[btn_i] = flow['macrotick_id']
                            break


class streamListWidget(QListWidget):
    def __init__(self,
                 parent,
                 main_window,
                 paint_widget):
        super().__init__(parent)

        self.parent = parent
        self.main_window = main_window
        self.paint_widget = paint_widget

        self.setGeometry(10, 50, 125, 550)

        self.stream_set = []
        self.path_set = []
        self.plan_set = []

        self.stream_item_list = []

        # itemClicked信号与槽函数绑定
        self.itemClicked.connect(self.clickItemRepaint)

        self.show()

    def set_stream_path_plan_list(self,
                                  stream_set,
                                  path_set,
                                  plan_set):
        self.stream_set = stream_set
        self.path_set = path_set
        self.plan_set = plan_set

        for stream, path in zip(stream_set, path_set):
            item = streamListWidgetItem(stream,
                                        self,
                                        path,
                                        self.paint_widget,
                                        self.main_window)
            self.stream_item_list.append(item)

    def clickItemRepaint(self, item: streamListWidgetItem):
        # print('click')
        # 重设画笔，画笔颜色与该item的主题颜色相同
        item.paint_widget.repaint_pen = QPen(item.color, 2, Qt.SolidLine)
        item.paint_widget.repaint_color = item.color
        # 设置需要重画的TSNButton对
        item.paint_widget.repaint_button_path = item.button_pair_path
        item.paint_widget.repaint_button_reverse_path = item.button_pair_reverse_path
        item.paint_widget.update()

        # 将本item的路径加入到label中
        # print(item.path)
        path_string = '流量%s的路径: %s' % (item.label, item.path[0][0])
        for p in item.path[1:]:
            s = '->%s' % p[0]
            path_string += s
        s = '->%s' % item.path[-1][1]
        path_string += s

        item.main_window.flowLabelWidget.clear()
        item.main_window.flowLabelWidget.setText(item.main_window.flowFeaturePage_label_text +
                                                 '\n' +
                                                 path_string)
        item.main_window.flowLabelWidget.show()

        # 将本item的发送时间槽加入到topoPaintWidget中
        # for (sl, btn) in zip(item.sw_label,
        #                      item.main_window.tsn_button_set):
            # btn.sched_label.setText(sl)       # 设备上写的时间槽数值
            # color = item.color.getRgb()
            # btn.sched_label.setStyleSheet("QLabel{font-size:24px;color:red;font:bold;"
            #                               "background-color: rgb(255, 255, 255);}")


class topoListWidgetItem(QListWidgetItem):
    def __init__(self,
                 label,
                 parent,
                 main_window):
        super(topoListWidgetItem, self).__init__('%s' % label, parent)

        self.label = label[-1]
        # self.sizeHint()
        # self.setSizeHint(QSize(120, 30))
        font = QFont()
        font.setPointSize(13)
        self.setFont(font)


class topoListWidget(QListWidget):
    def __init__(self,
                 parent,
                 main_window,
                 tab_widget):
        super(topoListWidget, self).__init__(parent)

        self.parent = parent
        self.main_window = main_window

        self.tab_widget = tab_widget

        self.setGeometry(10, 50, 105, 550)

        self.sw_list = []
        self.sw_item_list = []

        # 绑定槽函数
        self.itemClicked.connect(self.clickItemRegenerateTbl)

        self.show()

    def set_sw_item(self,
                    sw_list):
        # print(sw_list)s
        tmp_sw_list = sw_list
        sw_list = [int(i['node_id']) for i in sw_list]
        sw_list.sort()
        sw_list = [str(i) for i in sw_list]
        self.sw_list = sw_list
        # 添加交换机节点
        for node in self.sw_list:
            for entry in tmp_sw_list:
                if node == entry['node_id']:
                    label = entry['node_type'] + node
                    item = topoListWidgetItem(label,
                                              self,
                                              self.main_window)
                    self.sw_item_list.append(item)

    def clickItemRegenerateTbl(self, item):
        # 将tab页面的currentItem设为当前item
        # self.currentItem = item
        self.main_window.currentSWItem = item.label

        main_window = self.main_window
        tabWidget = main_window.ui.tabWidget

        index = tabWidget.currentIndex()
        # 注入时刻表
        if index == 0:
            tbl_cnt_node = []
            node_id = item.label
            for node in main_window.inject_tbl:
                if node['node_id'] == node_id:
                    tbl_cnt_node = node['entry_set']
            if tbl_cnt_node:
                rows = len(tbl_cnt_node)
                cols = len(tbl_cnt_node[0])

                model = QStandardItemModel(rows, cols)
                model.setHorizontalHeaderLabels(['dbuf_id', 'time_slot'])
                for row in range(rows):
                    col_cnt = 0
                    for col in ['dbuf_id', 'time_slot']:
                        it = QStandardItem(tbl_cnt_node[row][col])
                        model.setItem(row, col_cnt, it)
                        col_cnt += 1

                main_window.ui.injectTbl.setModel(model)
            else:
                model = QStandardItemModel()
                main_window.ui.injectTbl.setModel(model)
        # 映射表
        elif index == 1:
            tbl_cnt_node = []
            node_id = item.label
            for node in main_window.map_tbl:
                if node['node_id'] == node_id:
                    tbl_cnt_node = node['entry_set']
            # print(tbl_cnt_node)
            if tbl_cnt_node:
                rows = len(tbl_cnt_node)
                cols = len(tbl_cnt_node[0])

                model = QStandardItemModel(rows, cols)
                model.setHorizontalHeaderLabels(['flow_id', 'five_tuple_mask',
                                                 'ip_protocol', 'dst_port', 'src_port',
                                                 'dst_ip', 'src_ip'])
                # model.setVerticalHeaderLabels()
                for row in range(rows):
                    col_cnt = 0
                    for col in ['flow_id', 'five_tuple_mask',
                                'ip_protocol', 'dst_port', 'src_port',
                                'dst_ip', 'src_ip']:
                        it = QStandardItem(tbl_cnt_node[row][col])
                        model.setItem(row, col_cnt, it)
                        col_cnt += 1

                main_window.ui.mapTbl.setModel(model)
            else:
                model = QStandardItemModel()
                main_window.ui.mapTbl.setModel(model)
        # 转发表
        elif index == 2:
            tbl_cnt_node = []
            node_id = item.label
            for node in main_window.fwd_tbl:
                if node['node_id'] == node_id:
                    tbl_cnt_node = node['entry_set']
            # print(tbl_cnt_node)
            if tbl_cnt_node:
                rows = len(tbl_cnt_node)
                cols = len(tbl_cnt_node[0])

                model = QStandardItemModel(rows, cols)
                model.setHorizontalHeaderLabels(['flow_id', 'outport',
                                                 'priority', 'flow_period',
                                                 'inject_time_slot'])
                # model.setVerticalHeaderLabels()
                for row in range(rows):
                    col_cnt = 0
                    for col in ['flow_id', 'outport',
                                'priority', 'flow_period',
                                'inject_time_slot']:
                        it = QStandardItem(tbl_cnt_node[row][col])
                        model.setItem(row, col_cnt, it)
                        col_cnt += 1

                main_window.ui.forwardTbl.setModel(model)
            else:
                model = QStandardItemModel()
                main_window.ui.forwardTbl.setModel(model)
        # GCL门控表
        elif index == 3:
            tbl_cnt_node = []
            node_id = item.label
            for node in main_window.gcl_tbl:
                if node['node_id'] == node_id:
                    tbl_cnt_node = node['entry_set']
            # print(tbl_cnt_node)
            if tbl_cnt_node:
                rows = len(tbl_cnt_node)
                cols = len(tbl_cnt_node[0])

                model = QStandardItemModel(rows, cols)
                model.setHorizontalHeaderLabels(['port_id', 'slot', 'gate', ])
                # model.setVerticalHeaderLabels()
                for row in range(rows):
                    col_cnt = 0
                    for col in ['port_id', 'slot', 'gate', ]:
                        it = QStandardItem(tbl_cnt_node[row][col])
                        model.setItem(row, col_cnt, it)
                        col_cnt += 1

                main_window.ui.gclTbl.setModel(model)
            else:
                model = QStandardItemModel()
                main_window.ui.gclTbl.setModel(model)
        # 参数表
        elif index == 4:
            tbl_cnt_node = {}
            node_id = item.label
            for node in main_window.para_tbl:
                if node['node_id'] == node_id:
                    tbl_cnt_node = node
            # print(tbl_cnt_node)
            if tbl_cnt_node:
                rows = 1
                cols = 1 # len(tbl_cnt_node)

                model = QStandardItemModel(rows, cols)
                model.setVerticalHeaderLabels(['qbv_or_qch',
                                               'committed_information_rate',
                                               'schedule_period',
                                               'cyclelen',
                                               'time_slot_len'])

                row_cnt = 0
                for row in ['qbv_or_qch',
                            'committed_information_rate',
                            'schedule_period',
                            'cyclelen',
                            'time_slot_len']:
                    if row in tbl_cnt_node.keys():
                        it = QStandardItem(tbl_cnt_node[row])
                    else:
                        it = QStandardItem()
                    model.setItem(row_cnt, 0, it)
                    row_cnt += 1

                main_window.ui.paraTbl.setModel(model)
            else:
                model = QStandardItemModel()
                main_window.ui.paraTbl.setModel(model)
        # 帧消除表
        elif index == 5:
            tbl_cnt_node = []
            node_id = item.label
            for node in main_window.cb_tbl:
                if node['node_id'] == node_id:
                    tbl_cnt_node = node['entry_set']
            # print(tbl_cnt_node)
            if tbl_cnt_node:
                rows = len(tbl_cnt_node)
                cols = len(tbl_cnt_node[0])

                model = QStandardItemModel(rows, cols)
                model.setHorizontalHeaderLabels(['flow_id', 'ci'])
                for row in range(rows):
                    col_cnt = 0
                    for col in ['flow_id', 'ci']:
                        it = QStandardItem(tbl_cnt_node[row][col])
                        model.setItem(row, col_cnt, it)
                        col_cnt += 1

                main_window.ui.cbTbl.setModel(model)
            else:
                model = QStandardItemModel()
                main_window.ui.cbTbl.setModel(model)

